﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls.WebParts;

using SharePointHarvester.Events;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;


namespace SharePointHarvester {
    public class SiteHarvester {
        #region Data
        private string _url;
        private bool _recursive = true;
        private SPListTemplateType _listTemplateFilter = SPListTemplateType.InvalidType;

        #region Events
        /// <summary>
        /// Event Handler when a web is found
        /// </summary>
        public event WebFoundHandler WebFound;
        /// <summary>
        /// Event Handler when a Content Type is found for a web
        /// </summary>
        public event ContentTypeFoundHandler ContentTypeFound;
        /// <summary>
        /// Event Handler when a Feature is found for a web
        /// </summary>
        public event FeatureFoundHandler FeatureFound;
        /// <summary>
        /// Event Handler when a Feature is found for a web
        /// </summary>
        public event ListFoundHandler ListFound;
        /// <summary>
        /// Private Event Handler when an item is found for a list in a web
        /// </summary>
        private event ListItemFoundHandler _itemFound;
        /// <summary>
        /// Public property for the event handler.  Implements chaining to 
        /// the ListFound event.  Because in order to find a ListItem you must 
        /// find a list first.  So when a user subscribes to the ItemFound event
        /// he automatically subscribes to the ListFound event.
        /// </summary>
        public event ListItemFoundHandler ItemFound {
            add {
                _itemFound += value;
                ListFound += ( sender, e ) => {};
            }
            remove {
                _itemFound -= value;
                ListFound -= ( sender, e ) => { };
            }
        }
        /// <summary>
        /// Event Handler when a File is found in a list in a web
        /// </summary>
        private event ListItemFoundHandler _fileFound;
        /// <summary>
        /// Public property for the event handler.  Implements chaining to 
        /// the ListFound event.  Because in order to find a File you must 
        /// find a List first.  So when a user subscribes to the FileFound event
        /// he automatically subscribes to the ListFound.
        /// </summary>
        public event ListItemFoundHandler FileFound {
            add {
                _fileFound += value;
                ListFound += ( sender, e ) => { };
            }
            remove {
                _fileFound -= value;
                ListFound -= ( sender, e ) => { };
            }
        }
        /// <summary>
        /// Event Handler when an ASPX Page is found in a list in a web
        /// </summary>
        private event ListItemFoundHandler _pageFound;
        /// <summary>
        /// Public property for the event handler.  Implements chaining to 
        /// the ItemFound event.  Because in order to find an ASPX Page you must 
        /// find an Item first.  So when a user subscribes to the PageFound event
        /// he automatically subscribes to the ItemFound and hence ListFound.
        /// </summary>
        public event ListItemFoundHandler PageFound {
            add {
                _pageFound += value;
                ItemFound += ( sender, e ) => { };
            }
            remove {
                _pageFound -= value;
                ItemFound -= ( sender, e ) => { };
            }
        }

        /// <summary>
        /// Event Handler when a WebPart is found in a list in a web
        /// </summary>
        private event WebPartFoundHandler _webPartFound;
        /// <summary>
        /// Public property for the event handler.  Implements chaining to 
        /// the PageFound event.  Because in order to find a WebPart you must 
        /// find an ASPX Page first.  So when a user subscribes to the WebPartFound event
        /// he automatically subscribes to the PageFound, ItemFound, and ListFound events.
        /// </summary>
        public event WebPartFoundHandler WebPartFound {
            add {
                _webPartFound += value;
                PageFound += ( sender, e ) => { };
            }
            remove {
                _webPartFound -= value;
                PageFound -= ( sender, e ) => { };
            }
        }

        #endregion Events

        #endregion Data

        #region Constructors
        /// <summary>
        /// Default Constructor
        /// </summary>
        public SiteHarvester() : this( "", false ) {
        }

        /// <summary>
        /// Value Constructor
        /// </summary>
        /// <param name="url">URL to start with</param>
        /// <param name="recusrsive">Recurse all sub sites or not</param>
        public SiteHarvester( string url, bool recusrsive )
            : this( url, recusrsive, SPListTemplateType.InvalidType ) {
        }

        /// <summary>
        /// Full value constructor
        /// </summary>
        /// <param name="url">URL to start with</param>
        /// <param name="recusrsive">Recurse all sub sites or not</param>
        /// <param name="listTemplateType">Search for specific lists types.  Used only if ListFound event is handled</param>
        public SiteHarvester( string url, bool recusrsive, SPListTemplateType listTemplateType ) {
            _url = url;
            _recursive = recusrsive;
            _listTemplateFilter = listTemplateType;
        }

        #endregion Constructors

        #region Properties
        /// <summary>
        /// Gets or sets the URL for the site or web from which you wish to start harvesting.
        /// </summary>
        public string Url {
            get { return _url; }
            set { _url = value; }
        }

        /// <summary>
        /// Gets or sets whether harvesting for the URL is done at the URL or recursively for all sub sites too.
        /// </summary>
        public bool Recursive {
            get { return _recursive; }
            set { _recursive = value; }
        }

        /// <summary>
        /// Sets or gets the Filter to be used for the type of list to find if the ListFound Event is subscribed to.
        /// If no value is set or if SPListTemplateType.InvalidType then all lists are returned  if the ListFound event is
        /// subscribed to.
        /// </summary>
        public SPListTemplateType ListTemplateFilter {
            get { return _listTemplateFilter; }
            set { _listTemplateFilter = value; }
        }

        #endregion Properties

        #region Public Methods
        /// <summary>
        /// Gets the next web by recursing through the tree.  Uses yield return.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SPWeb> GetNextWeb( SPWeb rootWeb ) {
            SPSite spSite = new SPSite( _url );
            using( spSite ) {
                if( rootWeb == null ) {
                    rootWeb = spSite.OpenWeb();
                }
                using( rootWeb ) {
                    yield return rootWeb; //Return yourself first

                    if( _recursive ) {
                        foreach( SPWeb subWeb in rootWeb.Webs ) { //Now return each web you have
                            using( subWeb ) {
                                foreach( SPWeb subSubWeb in GetNextWeb( subWeb ) ) { //For each web you have recurse its children
                                    using( subSubWeb ) {
                                        yield return subSubWeb;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Runs the harvestor.  Events will be thrown as webs, lists, items are found
        /// </summary>
        public void RunHarvester() {
            SPSite spSite = new SPSite( _url );
            using( spSite ) {
                SPWeb rootWeb = spSite.OpenWeb();

                using( rootWeb ) {
                    HarvestWeb( rootWeb );
                }
            }
        }

        #endregion Public Methods

        #region Protected Methods
        #region Harvestor
        /// <summary>
        /// Recurses through the entire structure of the web.  Returns each web and fires an even also.
        /// Note this method uses yield return.
        /// </summary>
        /// <param name="webToHarvest"></param>
        /// <returns></returns>
        protected void HarvestWeb( SPWeb webToHarvest ) {
            if( webToHarvest != null ) {
                ProcesWeb( webToHarvest );

                if( _recursive ) {
                    foreach( SPWeb subWeb in webToHarvest.Webs ) { //Now return each web you have
                        using( subWeb ) {
                            HarvestWeb( subWeb );
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Does all processing once a web is found including invoking the WebFound event calling the
        /// OnWebFound virtual Method, and starting the search for lists and items etc
        /// </summary>
        /// <param name="webToHarvest"></param>
        protected void ProcesWeb( SPWeb webToHarvest ) {
            //Inform the world that a web is found
            if( WebFound != null ) {
                WebFound.Invoke( this, new WebFoundArg( webToHarvest ) );
            }
            OnWebFound( webToHarvest );
            //Look for ContentTypes
            FindContentTypes( webToHarvest );
            //Look for Features
            FindFeatures( webToHarvest );
            //Look for Lists
            FindLists( webToHarvest );
        }

        /// <summary>
        /// Finds content types for the web but only if user has subsribed to the ContentTypeFound event
        /// </summary>
        /// <param name="webToHarvest"></param>
        protected void FindContentTypes( SPWeb webToHarvest ) {
            if( ContentTypeFound != null ) { //look only if someone has subscribed to this event
                foreach( SPContentType contentType in webToHarvest.ContentTypes ) {
                    ContentTypeFound.Invoke( this, new ContentTypeFoundEventArg( webToHarvest, contentType ) );
                    OnContentTypeFound( webToHarvest, contentType );
                }
            }
        }

        /// <summary>
        /// Finds Features for the web but only if user has subsribed to the FeatureFound event
        /// </summary>
        /// <param name="webToHarvest"></param>
        protected void FindFeatures( SPWeb webToHarvest ) {
            if( FeatureFound != null ) { //look only if someone has subscribed to this event
                foreach( SPFeature feature in webToHarvest.Features ) {
                    FeatureFound.Invoke( this, new FeatureFoundEventArg( webToHarvest, feature ) );
                    OnFeatureFound( webToHarvest, feature );
                }
            }
        }

        /// <summary>
        /// Finds Lists for the web but only if user has subsribed to the ListFound event
        /// </summary>
        /// <param name="webToHarvest"></param>
        protected void FindLists( SPWeb webToHarvest ) {
            if( ListFound != null ) { //look only if someone has subscribed to this event
                foreach( SPList list in webToHarvest.Lists ) {
                    ProcessListFound( webToHarvest, list );
                }
            }
        }

        /// <summary>
        /// For each list found we check to see if the ListFound event needs to be fired or not.
        /// </summary>
        /// <param name="webToHarvest"></param>
        /// <param name="list"></param>
        private void ProcessListFound( SPWeb webToHarvest, SPList list ) {
            if( list.BaseTemplate == _listTemplateFilter || _listTemplateFilter == SPListTemplateType.InvalidType ) {
                ListFound.Invoke( this, new ListFoundEventArg( webToHarvest, list ) );
                OnListFound( webToHarvest, list, _listTemplateFilter );
                //Now find items
                if( _fileFound != null || _itemFound != null ) { //only if Events are subscribed too
                    FindItems( webToHarvest, list );
                }
            }
        }

        /// <summary>
        /// For a list loops through all items
        /// </summary>
        /// <param name="webToHarvest"></param>
        /// <param name="list"></param>
        private void FindItems( SPWeb webToHarvest, SPList list ) {
            if( _fileFound != null ) {
                FindFiles( webToHarvest, list );
            }
            if( _itemFound != null ) {
                //For all other items Loop through Items
                foreach( SPListItem item in list.Items ) {
                    _itemFound.Invoke( this, new ListItemFoundEventArg( webToHarvest, list, item ) );
                    OnListItemFound( webToHarvest, list, item );
                    //Check for ASPX Pages
                    if( _pageFound != null ) {
                        FindPages( webToHarvest, list, item );
                    }
                }
            }
        }

        /// <summary>
        /// Finds files
        /// </summary>
        /// <param name="webToHarvest"></param>
        /// <param name="list"></param>
        private void FindFiles( SPWeb webToHarvest, SPList list ) {
            //Handle Documents and pictures differently
            if( list.BaseTemplate == SPListTemplateType.DocumentLibrary || list.BaseTemplate == SPListTemplateType.PictureLibrary ) {
                foreach( SPListItem item in list.Items ) {
                    _itemFound.Invoke( this, new ListItemFoundEventArg( webToHarvest, list, item, item.File ) );
                    OnFileFound( webToHarvest, list, item, item.File );
                }
            }
        }

        /// <summary>
        /// Finds Pages/Webpart Pages for the web but only if user has subsribed to the PageFound event
        /// </summary>
        /// <param name="webToHarvest"></param>
        /// <param name="list"></param>
        /// <param name="item"></param>
        private void FindPages( SPWeb webToHarvest, SPList list, SPListItem item ) {
            if( item.File != null && item.File.Name.ToLower().EndsWith( "aspx" ) ) {
                _pageFound.Invoke( this, new ListItemFoundEventArg( webToHarvest, list, item, item.File ) );
                OnPageFound( webToHarvest, list, item, item.File );
                //Look for WebParts
                if( _webPartFound != null ) {
                    FindWebParts( webToHarvest, list, item, item.File );
                }
            }

        }

        /// <summary>
        /// Finds webparts on a page if any
        /// </summary>
        /// <param name="webToHarvest"></param>
        /// <param name="list"></param>
        /// <param name="item"></param>
        /// <param name="page"></param>
        private void FindWebParts( SPWeb webToHarvest, SPList list, SPListItem item, SPFile page ) {
            using( SPLimitedWebPartManager limitedWebpartMgr = page.GetLimitedWebPartManager( PersonalizationScope.Shared ) ) {
                foreach( System.Web.UI.WebControls.WebParts.WebPart webpart in limitedWebpartMgr.WebParts ) {
                    _webPartFound.Invoke( this, new WebPartFoundEventArg( webToHarvest, list, item, page, webpart ) );
                    OnWebPartFound( webToHarvest, list, item, page, webpart );
                }
            }
        }

        #endregion Harvestor

        #region Virtual Methods

        /// <summary>
        /// Virtual Method called when a web is found by the harvestor
        /// </summary>
        /// <param name="web"></param>
        protected virtual void OnWebFound( SPWeb web ) {

        }

        /// <summary>
        /// Virtual Method called when a content type is found in a web by the harvestor
        /// </summary>
        /// <param name="web"></param>
        /// <param name="contentType"></param>
        protected virtual void OnContentTypeFound( SPWeb web, SPContentType contentType ) {

        }

        /// <summary>
        /// Virtual Method called when a Feature is found in a web by the harvestor
        /// </summary>
        /// <param name="web"></param>
        /// <param name="feature"></param>
        protected virtual void OnFeatureFound( SPWeb web, SPFeature feature ) {

        }

        /// <summary>
        /// Virtual Method called when a List is found in a web by the harvestor
        /// </summary>
        /// <param name="web"></param>
        /// <param name="list"></param>
        /// <param name="listTemplateType"></param>
        protected virtual void OnListFound( SPWeb web, SPList list, SPListTemplateType listTemplateType ) {

        }

        /// <summary>
        /// Virtual Method called when a List is found in a web by the harvestor
        /// </summary>
        /// <param name="web"></param>
        /// <param name="list"></param>
        /// <param name="item"></param>
        protected virtual void OnListItemFound( SPWeb web, SPList list, SPListItem item ) {

        }

        /// <summary>
        /// Virtual Method called when a File is found in a List in a web by the harvestor
        /// </summary>
        /// <param name="web"></param>
        /// <param name="list"></param>
        /// <param name="item"></param>
        /// <param name="file"></param>
        protected virtual void OnFileFound( SPWeb web, SPList list, SPListItem item, SPFile file ) {

        }

        /// <summary>
        /// Virtual Method called when an aspx Page is found in a List in a web by the harvestor
        /// </summary>
        /// <param name="web"></param>
        /// <param name="list"></param>
        /// <param name="item"></param>
        /// <param name="file"></param>
        protected virtual void OnPageFound( SPWeb web, SPList list, SPListItem item, SPFile file ) {

        }

        /// <summary>
        ///  Virtual Method called when a webpart is found on an aspx Page in a List in a web by the harvestor
        /// </summary>
        /// <param name="web"></param>
        /// <param name="list"></param>
        /// <param name="item"></param>
        /// <param name="file"></param>
        /// <param name="webpart"></param>
        protected virtual void OnWebPartFound( SPWeb web, SPList list, SPListItem item, SPFile file, System.Web.UI.WebControls.WebParts.WebPart webpart ) {

        }
        #endregion Virtual Methods

        #endregion Protected Methods

    }
}
